﻿using System.Windows;
using System;
using System.Collections.Generic;
using DeepEarth.Core.Utilities;
using System.ComponentModel;
using DeepEarth.Core.Converters;
namespace DeepEarth.Core
{
    [TypeConverter(typeof(LocationRectangleTypeConverter))]
    public class LocationRectangle
    {
        public double North { get; set; }
        public double South { get; set; }
        public double East { get; set; }
        public double West { get; set; }

        public Location NorthWest
        {
            get
            {
                return new Location { Latitude  = North, Longitude = West };
            }
        }

        public Location SouthEast
        {
            get
            {
                return new Location { Latitude = South, Longitude = East };
            }
        }

        public Location Center
        {
            get
            {
                double longitude = (this.East + this.West) / 2.0;
                double latitude = (this.North + this.South) / 2.0;

                Location center = new Location
                {
                    Latitude = latitude,
                    Longitude = longitude
                };

                return center;
            }
        }

        public double Width
        {
            get
            {
                if (!SpansDateline)
                {
                    return (Math.Abs(this.East - this.West));
                }
                else
                {
                    // Bounding box spans the dateline
                    return (East - West + 360);
                }
            }          
        }

        public double Height
        {
            get
            {
                return (Math.Abs(this.North - this.South));
            }
        }

        public double MaxDimension
        {
            get { return Math.Max(Width, Height); }
        }
            
        private bool SpansDateline
        {
            get
            {
                return East < West;
            }
        }

        public LocationRectangle(Location northWest, Location southEast)
        {
            North = northWest.Latitude;
            West = northWest.Longitude;
            South = southEast.Latitude;
            East = southEast.Longitude;
        }

        public LocationRectangle(IEnumerable<Location> locations)
        {
            North = -90;
            South = 90;
            West = 180;
            East = -180;

            foreach (Location location in locations)
            {
                North = Math.Max(North, location.Latitude);
                South = Math.Min(South, location.Latitude);
                West = Math.Min(West, location.Longitude);
                East = Math.Max(East, location.Longitude);
            }
        }

        private LocationRectangle()
        {
        }

        public bool Collides(LocationRectangle other)
        {
            var b = CollisionRectangle(other);
            var a = CollisionRectangle(this);

            //// Flip rectangle along the X axis, because this fast intersection uses System.Windows.Rect where the Y value increases as you head south/down!
            //Rect a = new Rect(simpleThis.West, simpleThis.South, simpleThis.Width, simpleThis.Height);
            //Rect b = new Rect(simpleOther.West, simpleOther.South, simpleOther.Width, simpleOther.Height);

            a.Intersect(b);

            return !a.IsEmpty;
        }

        public LocationRectangle Merge(LocationRectangle other)
        {
            return new LocationRectangle
            {
                North = Math.Max(this.North, other.North),
                South = Math.Min(this.South, other.South),
                West = Math.Min(this.West, other.West),
                East = Math.Max(this.East, other.East)
            };
        }

        public bool Contains(Location location)
        {
            var simpleThis = GetSimplifiedBounds(this);

            double compareLat = location.Latitude;
            
            // if this does not span the dateline, it has been shifted 360 degrees to the right
            // do the same to the location
            double compareLon = !this.SpansDateline ? location.Longitude + 360 : location.Longitude;

            return (simpleThis.North >= compareLat && simpleThis.South <= compareLat) && (simpleThis.West <= compareLon && simpleThis.East >= compareLon);
        }

        public bool Contains (LocationRectangle boundingBox)
        {
            var simpleThis = CollisionRectangle(this);
            var simpleOther = CollisionRectangle(boundingBox);

            return simpleThis.Top < simpleOther.Top &&
                simpleThis.Bottom > simpleOther.Bottom &&
                simpleThis.Left < simpleOther.Left &&
                simpleThis.Right > simpleOther.Right;
        }

        // Returns a bounding box where certains bounds calculations are simple (i.e. doesn't wrap)
        LocationRectangle GetSimplifiedBounds(LocationRectangle rectangle)
        {
            var simple = new LocationRectangle();
            simple.North = rectangle.North;
            simple.South = rectangle.South;
            simple.West = rectangle.West;

            simple.East = 360 + rectangle.East;

            // if we don't span the dateline, represent the entire bounds in the 180 - 540 degree space.
            if (!rectangle.SpansDateline)
            {
                simple.West = 360 + rectangle.West;
            }

            return simple;
        }

        Rect CollisionRectangle(LocationRectangle rectangle)
        {
            double left = rectangle.West;
            double right = rectangle.East;

            if (rectangle.SpansDateline)
            {
                right = rectangle.East + 360;
            }

            double top = rectangle.South;
            double bottom = rectangle.North;

            return new Rect(new Point(left, top), new Point(right, bottom));
        }
    }
}
